﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CourierManager;
using System.Collections.ObjectModel;
using TeamWPF.Extensions;

namespace TeamWPF.Simulator
{
    public static class Simulator
    {
        public static void UnloadTransportUnits(bool unloadAllUnit)
        {
            List<Freight> allFreights = LoadAllFreights();
            List<TransportUnit> allVehicles = LoadAllVehicles();
            List<Route> allRoutes = LoadAllRoutes();
            List<Driver> allDrivers = LoadAllDrivers();
            List<Employee> allEmployees = LoadAllEmployees();
            var dispatchedFreights = allFreights.Where(x => x.State == FreightState.Dispatched);
            if (dispatchedFreights.Count() == 0)
            {
                ErrorWindow noFreightsWindow = new ErrorWindow();
                noFreightsWindow.ErrorText.Text = "No dispatched packets.";
                noFreightsWindow.ShowDialog();
            }
            bool loadOnlyOneVehicle = false;
            while (dispatchedFreights.Count() > 0 && !loadOnlyOneVehicle)
            {
                TransportUnit selectedVehicle = SelectVehicle(allVehicles, TransportUnitState.OnACourse);
                if (selectedVehicle == null)
                {
                    ErrorWindow noVehiclesWindow = new ErrorWindow();
                    noVehiclesWindow.ErrorText.Text = "No vehicles on a course.";
                    noVehiclesWindow.ShowDialog();
                    break;
                }
                Location reachedDestination = SelectDestination(selectedVehicle.CurrentRoute);
                File.WriteAllText(@"../../../CourierManager/ListOfBillOfLadings.txt", String.Empty);
                int countPackets = 0;
                for (int number = 0; number < allFreights.Count; number++)
                {
                    allFreights[number].SerialNumber = number + 1;
                    if (allFreights[number].State == FreightState.Dispatched
                        && allFreights[number].LoadedVehicle.LicensePlate==selectedVehicle.LicensePlate)
                    {
                        if (reachedDestination==allFreights[number].Destination)
                        {
                            int allDestinations = selectedVehicle.CurrentRoute.Locations.Count();
                            if (selectedVehicle.CurrentRoute.Locations[allDestinations - 1] == reachedDestination)
                            {
                                selectedVehicle.State = TransportUnitState.Available;
                                selectedVehicle.CurrentDriver.IsOnACourse = false;
                            }


                            allFreights[number].State = FreightState.Delivered;
                            selectedVehicle.Load -= allFreights[number].Weight;
                            countPackets++;
                        }
                        else
                        {
                            selectedVehicle.LoadedFreights.Add(allFreights[number]);
                        }
                    }
                    allFreights[number].WriteInDatabase();
                }
                if (countPackets == 0)
                {
                    ErrorWindow noPacketForThisCourse = new ErrorWindow();
                    noPacketForThisCourse.ErrorText.Text = "No packets for this destination";
                    noPacketForThisCourse.ShowDialog();

                }
                else
                {
                    if (!selectedVehicle.CurrentDriver.IsOnACourse)
                    {
                        File.WriteAllText(@"../../../CourierManager/Employees.txt", String.Empty);
                        foreach (Employee employee in allEmployees)
                        {
                            if (employee.IDNumber != selectedVehicle.CurrentDriver.IDNumber)
                            {
                                employee.SaveEmployeeData();
                            }
                            else
                            {
                                selectedVehicle.CurrentDriver.SaveEmployeeData();
                            }
                        }
                    }
                    
                    selectedVehicle.WriteInDatabase();
                    InfoWindow infoForVehicle = new InfoWindow();
                    infoForVehicle.LicensePlateBlock.Text = selectedVehicle.LicensePlate;
                    infoForVehicle.LastLineText.Text = "contain freights as follows:";
                    ObservableCollection<Freight> loadedFreights = new ObservableCollection<Freight>();
                    foreach (Freight freight in selectedVehicle.LoadedFreights)
                    {
                        loadedFreights.Add(freight);
                    }
                    infoForVehicle.FreightData.DataContext = loadedFreights;
                    infoForVehicle.DriverInfo.Text = selectedVehicle.CurrentDriver.FirstName + " " + selectedVehicle.CurrentDriver.LastName;
                    infoForVehicle.ShowDialog();
                    foreach (TransportUnit vehicle in allVehicles)
                    {
                        if (selectedVehicle.LicensePlate == vehicle.LicensePlate)
                        {
                            vehicle.State = selectedVehicle.State;
                        }
                    }
                    dispatchedFreights = allFreights.Where(x => x.State == FreightState.Dispatched);
                }
                if (!unloadAllUnit)
                {
                    loadOnlyOneVehicle = true;
                }
            }
        }

        private static Location SelectDestination(Route route)
        {
            SelectItemWindow locationChoice = new SelectItemWindow();
            locationChoice.AdviceTextBlock.Text = "Select the reached location from the list below:";
            locationChoice.ListOfItems.ItemsSource=route.Locations ;
            locationChoice.ListOfItems.SelectedIndex = 0;
            locationChoice.ShowDialog();
            return (Location)locationChoice.ListOfItems.SelectedValue;
        }

        public static void LoadTransportUnits(bool loadAllUnits)
        {
            List<Freight> allFreights = LoadAllFreights();
            List<TransportUnit> allVehicles = LoadAllVehicles();
            List<Route> allRoutes = LoadAllRoutes();
            List<Driver> allDrivers = LoadAllDrivers();
            List<Employee> allEmployees = LoadAllEmployees();

            var processedFreights = allFreights.Where(x => x.State == FreightState.Processed);
            if (processedFreights.Count()==0)
            {
                ErrorWindow noFreightsWindow = new ErrorWindow();
                noFreightsWindow.ErrorText.Text = "No packets to dispatch.";
                noFreightsWindow.ShowDialog();
            }
            bool loadOnlyOneVehicle = false;
            while (processedFreights.Count()>0&&!loadOnlyOneVehicle)
            {
                TransportUnit selectedVehicle = SelectVehicle(allVehicles,TransportUnitState.Available);
                if (selectedVehicle==null)
                {
                    ErrorWindow noVehiclesWindow = new ErrorWindow();
                    noVehiclesWindow.ErrorText.Text = "No available vehicles.";
                    noVehiclesWindow.ShowDialog();
                    break;
                }
                Route selectedRoute = SelectRoute(allRoutes);
                selectedVehicle.CurrentRoute = selectedRoute;
                Driver selectedDriver = SelectDriver(allDrivers);
                if (selectedDriver==null)
                {
                    ErrorWindow noDriversWindow = new ErrorWindow();
                    noDriversWindow.ErrorText.Text = "No free drivers.";
                    noDriversWindow.ShowDialog();
                    break;
                }
                selectedVehicle.CurrentDriver = selectedDriver;
                File.WriteAllText(@"../../../CourierManager/ListOfBillOfLadings.txt", String.Empty);
                int countPackets = 0;
                for (int number = 0; number < allFreights.Count; number++)
                {
                    allFreights[number].SerialNumber = number + 1;
                    if (allFreights[number].State == FreightState.Processed
                        && selectedVehicle.CurrentRoute.Locations.Contains(allFreights[number].Destination))
                    {
                        selectedVehicle.State = TransportUnitState.OnACourse;
                        selectedDriver.IsOnACourse = true;
                        if (selectedVehicle.AddLoad(allFreights[number].Weight))
                        {
                            allFreights[number].State = FreightState.Dispatched;
                            allFreights[number].LoadedVehicle = selectedVehicle;
                            selectedVehicle.LoadedFreights.Add(allFreights[number]);
                            countPackets++;
                        }
                        else
                        {
                            allFreights[number].WriteInDatabase();
                            break;
                        }
                    }
                    allFreights[number].WriteInDatabase();
                }
                if (countPackets == 0)
                {
                    ErrorWindow noPacketForThisCourse = new ErrorWindow();
                    noPacketForThisCourse.ErrorText.Text = "No packets for this course";
                    noPacketForThisCourse.ShowDialog();

                }
                else
                {
                    File.WriteAllText(@"../../../CourierManager/Employees.txt", String.Empty);
                    foreach (Employee employee in allEmployees)
                    {
                        if (employee.IDNumber != selectedDriver.IDNumber)
                        {
                            employee.SaveEmployeeData();
                        }
                        else
                        {
                            selectedDriver.SaveEmployeeData();
                        }
                    }
                    selectedVehicle.WriteInDatabase();
                    InfoWindow infoForVehicle = new InfoWindow();
                    infoForVehicle.LicensePlateBlock.Text = selectedVehicle.LicensePlate;
                    infoForVehicle.LastLineText.Text = "was loaded with freights as follows:";
                    ObservableCollection<Freight> loadedFreights = new ObservableCollection<Freight>();
                    foreach (Freight freight in selectedVehicle.LoadedFreights)
                    {
                        loadedFreights.Add(freight);
                    }
                    infoForVehicle.FreightData.DataContext = loadedFreights;
                    infoForVehicle.DriverInfo.Text = selectedDriver.FirstName + " " + selectedDriver.LastName;
                    infoForVehicle.ShowDialog();
                    foreach (TransportUnit vehicle in allVehicles)
                    {
                        if (selectedVehicle.LicensePlate == vehicle.LicensePlate)
                        {
                            vehicle.State = TransportUnitState.OnACourse;
                        }
                    }
                    processedFreights = allFreights.Where(x => x.State == FreightState.Processed);
                }
                if (!loadAllUnits)
                {
                    loadOnlyOneVehicle = true;
                }
            }
        }
  
        private static List<Employee> LoadAllEmployees()
        {
            Driver driver = new Driver();
            driver.PathToDataBase = @"../../../CourierManager/Employees.txt";
            List<string> allEmployeesAsString = driver.ReadEmployeeData();
            List<Employee> allEmployees = new List<Employee>();
            foreach (string employeeAsString in allEmployeesAsString)
            {
                allEmployees.Add(ReturnEmployeeFromString(employeeAsString));
            }
            return allEmployees;
        }
  
        private static List<Driver> LoadAllDrivers()
        {
            Driver driver = new Driver();
            driver.PathToDataBase = @"../../../CourierManager/Employees.txt";
            List<string> allEmployeesAsString = driver.ReadEmployeeData();
            List<Driver> allDrivers = new List<Driver>();
            foreach (string employeeAsString in allEmployeesAsString)
            {
                if (employeeAsString.Split('*')[0] == driver.GetType().Name)
                {
                    allDrivers.Add(ReturnDriverFromString(employeeAsString.Split('*')[4]));
                }
            }
            return allDrivers;
        }
  
        private static List<Route> LoadAllRoutes()
        {
            Route route = new Route();
            route.PathToDatabase = @"../../../CourierManager/Routes.txt";
            List<string> allRoutesAsString = route.LoadAllRoutes();
            List<Route> allRoutes = new List<Route>();
            foreach (string routeAsString in allRoutesAsString)
            {
                allRoutes.Add(ReturnRouteFromString(routeAsString.Split('*')[0]));
            }
            return allRoutes;
        }
  
        private static List<Freight> LoadAllFreights()
        {
            Freight currentFreight = new Freight();
            currentFreight.PathToDatabase = @"../../../CourierManager/ListOfBillOfLadings.txt";
            List<string> allFreightsAsString = currentFreight.LoadAllBillOfLading();
            List<Freight> allFreights = new List<Freight>();
            foreach (string freightAsString in allFreightsAsString)
            {
                allFreights.Add(ReturnFreightFromString(freightAsString));
            }
            return allFreights;
        }
  
        private static List<TransportUnit> LoadAllVehicles()
        {
            TransportUnit currentVehicle = new TransportUnit();
            currentVehicle.PathToDatabase = @"../../../CourierManager/TransportUnits.txt";
            List<string> allVehiclesAsString = currentVehicle.GetAllTransportUnits();
            List<TransportUnit> allVehicles = new List<TransportUnit>();
            foreach (string vehicleAsString in allVehiclesAsString)
            {
                allVehicles.Add(ReturnVehicleFromString(vehicleAsString));
            }
            return allVehicles;
        }

        private static Driver SelectDriver(List<Driver> allDrivers)
        {
            SelectItemWindow driverChoice = new SelectItemWindow();
            driverChoice.AdviceTextBlock.Text = "Select a driver from the list below:";
            var drivers = from driver in allDrivers
                          where driver.IsOnACourse==false
                         select new { DriverNames = driver.FirstName+" "+driver.LastName, CurrentDriver = driver };
            if (drivers.Count()!=0)
            {
                driverChoice.ListOfItems.ItemsSource = drivers;
                driverChoice.ListOfItems.DisplayMemberPath = "DriverNames";
                driverChoice.ListOfItems.SelectedValuePath = "CurrentDriver";
                driverChoice.ListOfItems.SelectedIndex = 0;
                driverChoice.InfoTextBlock.Text = "ID Number: " + ((Driver)driverChoice.ListOfItems.SelectedValue).IDNumber;
                driverChoice.ListOfItems.SelectionChanged += (send, ev) =>
                { driverChoice.InfoTextBlock.Text = "ID Number: " + ((Driver)driverChoice.ListOfItems.SelectedValue).IDNumber; };
                driverChoice.ShowDialog();
                return (Driver)driverChoice.ListOfItems.SelectedValue;
            }
            else
            {
                return null;
            }
        }

        private static Route SelectRoute(List<Route> allRoutes)
        {
            SelectItemWindow routeChoice = new SelectItemWindow();
            routeChoice.AdviceTextBlock.Text = "Select a route from the list below:";
            var routes = from route in allRoutes
                         select new { RouteName = route.Name, CurrentRoute = route };
            routeChoice.ListOfItems.ItemsSource = routes;
            routeChoice.ListOfItems.DisplayMemberPath = "RouteName";
            routeChoice.ListOfItems.SelectedValuePath = "CurrentRoute";
            routeChoice.ListOfItems.SelectedIndex = 0;
            routeChoice.InfoTextBlock.Text = "Destinations: " + String.Join(",", ((Route)routeChoice.ListOfItems.SelectedValue).Locations);
            routeChoice.ListOfItems.SelectionChanged += (send, ev) =>
                { routeChoice.InfoTextBlock.Text = "Destinations: " + String.Join(",", ((Route)routeChoice.ListOfItems.SelectedValue).Locations); };
            routeChoice.ShowDialog();
            return (Route)routeChoice.ListOfItems.SelectedValue;
        }

        private static TransportUnit SelectVehicle(List<TransportUnit> allVehicles,TransportUnitState state)
        {
            TransportUnit resultVehicle = new TransportUnit();
            SelectItemWindow vehicleChoice = new SelectItemWindow();
            vehicleChoice.AdviceTextBlock.Text = "Select an avaivable vehicle from the list below:";
            var selectedVehicles = from vehicle in allVehicles
                                    where vehicle.State == state
                                    select new { LicensePlate = vehicle.LicensePlate, Vehicle = vehicle };
            if (selectedVehicles.Count()!=0)
            {
                vehicleChoice.ListOfItems.ItemsSource = selectedVehicles;
                vehicleChoice.ListOfItems.DisplayMemberPath = "LicensePlate";
                vehicleChoice.ListOfItems.SelectedValuePath = "Vehicle";
                vehicleChoice.ListOfItems.SelectedIndex = 0;
                vehicleChoice.InfoTextBlock.Text = "Capacity:" + ((TransportUnit)vehicleChoice.ListOfItems.SelectedValue).Capacity.ToString();
                vehicleChoice.ListOfItems.SelectionChanged += (send, ev) =>
                { vehicleChoice.InfoTextBlock.Text = "Capacity:" + ((TransportUnit)vehicleChoice.ListOfItems.SelectedValue).Capacity.ToString(); };
                vehicleChoice.ShowDialog();
                resultVehicle = (TransportUnit)vehicleChoice.ListOfItems.SelectedValue;
                resultVehicle.PathToDatabase = @"../../../CourierManager/TransportUnits.txt";
                if (resultVehicle is Truck)
                {
                    return resultVehicle as Truck;
                    
                }
                else
                {
                    return resultVehicle as Van;
                }
            }
            else
            {
                return null;
            }
        }

        public static TransportUnit ReturnVehicleFromString(string vehicleData)
        {
            string licencePlate = vehicleData.Split('*')[1];
            TransportUnitState state = EnumHelper.ReturnValueFromTransportUnitState(vehicleData.Split('*')[3]);

            if (vehicleData.Split('*')[0].ToLower() == "truck")
            {
                Truck resultTruck = new Truck(licencePlate, state);
                resultTruck.PathToDatabase = @"../../../CourierManager/TransportUnits.txt";
                decimal vehicelLoad = decimal.Parse(vehicleData.Split('*')[2]);
                resultTruck.Type = vehicleData.Split('*')[0].ToString();
                resultTruck.Load = vehicelLoad;
                if (resultTruck.State == TransportUnitState.OnACourse)
                {
                    resultTruck.CurrentRoute = ReturnRouteFromString(vehicleData.Split('*')[4]);
                    resultTruck.CurrentDriver = ReturnDriverFromString(vehicleData.Split('*')[5]);
                }
                return resultTruck;
            }
            else 
            {
                Van resultVan = new Van(licencePlate, state);
                resultVan.PathToDatabase = @"../../../CourierManager/TransportUnits.txt";
                decimal vehicelLoad = decimal.Parse(vehicleData.Split('*')[2]);
                resultVan.Load = vehicelLoad;
                resultVan.Type = vehicleData.Split('*')[0].ToString();
                if (resultVan.State == TransportUnitState.OnACourse)
                {
                    Route currentRoute = ReturnRouteFromString(vehicleData.Split('*')[4]);
                    resultVan.CurrentRoute = currentRoute;
                    Driver currentDriver = ReturnDriverFromString(vehicleData.Split('*')[5]);
                    resultVan.CurrentDriver = currentDriver;
                }
                return resultVan;
            }
        }

        public static Employee ReturnEmployeeFromString(string employee)
        {
            string typeOfEmployee = employee.Split('*')[0];
            string firstName = employee.Split('*')[1];
            string lastName = employee.Split('*')[2];
            string address = employee.Split('*')[3];
            ulong idNumber = ulong.Parse(employee.Split('*')[4]);
            ulong phone = ulong.Parse(employee.Split('*')[5]);
            decimal salary = decimal.Parse(employee.Split('*')[6]);
            bool isOnACourse = false;
            if (typeOfEmployee=="Driver")
            {
                isOnACourse = bool.Parse(employee.Split('*')[7]);
            }
            switch (typeOfEmployee)
            {
                case "Driver":
                    return new Driver(firstName, lastName, address, idNumber, phone, salary, isOnACourse);
                case "Manager":
                    return new Manager(firstName, lastName, address, idNumber, phone, salary);
                case "Accountant":
                    return new Accountant(firstName, lastName, address, idNumber, phone, salary);
                case "CustomerServiceEmployee":
                    return new CustomerServiceEmployee(firstName, lastName, address, idNumber, phone, salary);
                default:
                    return null;
            }
        }

        public static Driver ReturnDriverFromString(string driverID)
        {
            Driver resultDriver = new Driver();
            resultDriver.PathToDataBase = @"../../../CourierManager/Employees.txt";
            List<string> allEmployees = resultDriver.ReadEmployeeData();
            foreach (string employee in allEmployees)
            {
                if (driverID == employee.Split('*')[4])
                {
                    string firstName = employee.Split('*')[1];
                    string lastName = employee.Split('*')[2];
                    string address = employee.Split('*')[3];
                    ulong idNumber = ulong.Parse(employee.Split('*')[4]);
                    ulong phone = ulong.Parse(employee.Split('*')[5]);
                    decimal salary = decimal.Parse(employee.Split('*')[6]);
                    bool isOnACourse = bool.Parse(employee.Split('*')[7]);
                    resultDriver = new Driver(firstName, lastName, address, idNumber, phone, salary, isOnACourse);
                }
            }
            return resultDriver;
        }

        private static Route ReturnRouteFromString(string routeName)
        {
            Route resultRoute = new Route();
            resultRoute.PathToDatabase = @"../../../CourierManager/Routes.txt";
            List<string> routes = resultRoute.LoadAllRoutes();
            List<Location> routeLocations = new List<Location>();
            foreach (string route in routes)
            {
                string[] routeDetails = route.Split('*');
                if (routeName == routeDetails[0])
                {
                    for (int location = 1; location < routeDetails.Length; location++)
                    {
                        routeLocations.Add((Location)Enum.Parse(typeof(Location), routeDetails[location]));
                    }
                }
            }
            return resultRoute = new Route(routeName, routeLocations);
        }

        public static Freight ReturnFreightFromString(string freightData)
        {
            string[] freightDetails = freightData.Split('*');
            decimal weight = decimal.Parse(freightDetails[5]);
            Customer sender = ReturnCustomerFromString(freightDetails[2]);
            Customer recipient = ReturnCustomerFromString(freightDetails[3]);
            Location destination = EnumHelper.ReturnValueFromLocation(freightDetails[1].Substring(10, 2));
            Freight currentFreight = new Freight(weight, sender, recipient, destination);
            currentFreight.State = (FreightState)Enum.Parse(typeof(FreightState), freightDetails[6]);
            if (freightDetails.Length>7)
            {
                currentFreight.LoadedVehicle = ReturnVehicleFromLicensePlate(freightDetails[7]);
            }
            currentFreight.PathToDatabase = @"../../../CourierManager/ListOfBillOfLadings.txt";
            return currentFreight;
        }
  
        private static TransportUnit ReturnVehicleFromLicensePlate(string licensePlate)
        {
            TransportUnit resultVehicle = new TransportUnit();
            resultVehicle.PathToDatabase = @"../../../CourierManager/TransportUnits.txt";
            List<string> allVehicles = resultVehicle.GetAllTransportUnits();
            foreach (string vehicle in allVehicles)
            {
                string[] vehicleDetails = vehicle.Split('*');
                if (vehicleDetails[1]==licensePlate)
                {
                        resultVehicle = ReturnVehicleFromString(vehicle);
                }
            }
            return resultVehicle;
        }

        public static Customer ReturnCustomerFromString(string customerData)
        {
            char customerType = customerData[customerData.Length - 1];
            customerData = customerData.Remove(customerData.Length - 1);
            int clientNumber = int.Parse(customerData);
            if (customerType == 'I')
            {
                IndividualCustomer wantedCustomer = new IndividualCustomer(0);
                wantedCustomer.PathToDatabase = @"../../../CourierManager/IndividualCustomers.txt";
                List<string> allCustomers = wantedCustomer.ReadClientData();
                foreach (string client in allCustomers)
                {
                    string[] customerDetails = client.Split('*');
                    if (int.Parse(customerDetails[0]) == clientNumber)
                    {
                        string firstName = customerDetails[1];
                        string lastName = customerDetails[2];
                        string address = customerDetails[3];
                        ulong idNumber = ulong.Parse(customerDetails[4]);
                        ulong phone = ulong.Parse(customerDetails[5]);
                        wantedCustomer = new IndividualCustomer(firstName, lastName, address, idNumber, phone, false);
                        break;
                    }
                }
                return wantedCustomer;
            }
            else
            {
                CompanyCustomer wantedCustomer = new CompanyCustomer(0);
                wantedCustomer.PathToDatabase = @"../../../CourierManager/CompanyCustomers.txt";
                List<string> allCustomers = wantedCustomer.ReadClientData();
                foreach (string client in allCustomers)
                {
                    string[] customerDetails = client.Split('*');
                    if (int.Parse(customerDetails[0]) == clientNumber)
                    {
                        string name = customerDetails[1];
                        string address = customerDetails[2];
                        ulong idNumber = ulong.Parse(customerDetails[3]);
                        ulong phone = ulong.Parse(customerDetails[4]);
                        wantedCustomer = new CompanyCustomer(name, address, idNumber, phone,false);
                        break;
                    }
                }
                return wantedCustomer;
            }
        }
    }
}
